using System;
using ch.ethz.ssh2.crypto.digest;
using ch.ethz.ssh2.log;
using ch.ethz.ssh2.packets;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
namespace ch.ethz.ssh2.signature
{

    /// <summary> DSASHA1Verify.
    /// 
    /// </summary>
    /// <author>  Christian Plattner
    /// </author>
    /// <version>  2.50, 03/15/10
    /// </version>
    public class DSASHA1Verify
    {
        //UPGRADE_NOTE: Final was removed from the declaration of 'log '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        //UPGRADE_NOTE: The initialization of  'log' was moved to static method 'ch.ethz.ssh2.signature.DSASHA1Verify'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
        private static readonly Logger log;

        public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key)
        {
            TypesReader tr = new TypesReader(key);

            System.String key_format = tr.readString();

            if (key_format.Equals("ssh-dss") == false)
                throw new System.ArgumentException("This is not a ssh-dss public key!");

            BigInteger p = tr.readMPINT();
            BigInteger q = tr.readMPINT();
            BigInteger g = tr.readMPINT();
            BigInteger y = tr.readMPINT();

            if (tr.remain() != 0)
                throw new System.IO.IOException("Padding in DSA public key!");

            return new DSAPublicKey(p, q, g, y);
        }

        public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk)
        {
            TypesWriter tw = new TypesWriter();

            tw.writeString("ssh-dss");
            tw.writeMPInt(pk.P);
            tw.writeMPInt(pk.Q);
            tw.writeMPInt(pk.G);
            tw.writeMPInt(pk.Y);

            return tw.getBytes();
        }

        public static byte[] encodeSSHDSASignature(DSASignature ds)
        {
            TypesWriter tw = new TypesWriter();

            tw.writeString("ssh-dss");

            //UPGRADE_ISSUE: Method 'java.math.BigInteger.toByteArray' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javamathBigIntegertoByteArray'"
            byte[] r = ds.R.ToByteArray();
            //UPGRADE_ISSUE: Method 'java.math.BigInteger.toByteArray' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javamathBigIntegertoByteArray'"
            byte[] s = ds.S.ToByteArray();

            byte[] a40 = new byte[40];

            /* Patch (unsigned) r and s into the target array. */

            int r_copylen = (r.Length < 20) ? r.Length : 20;
            int s_copylen = (s.Length < 20) ? s.Length : 20;

            Array.Copy(r, r.Length - r_copylen, a40, 20 - r_copylen, r_copylen);
            Array.Copy(s, s.Length - s_copylen, a40, 40 - s_copylen, s_copylen);

            tw.writeString(a40, 0, 40);

            return tw.getBytes();
        }

        public static DSASignature decodeSSHDSASignature(byte[] sig)
        {
            byte[] rsArray;

            if (sig.Length == 40)
            {
                rsArray = sig;
            }
            else
            {
                TypesReader tr = new TypesReader(sig);

                System.String sig_format = tr.readString();

                if (sig_format.Equals("ssh-dss") == false)
                    throw new System.IO.IOException("Peer sent wrong signature format");

                rsArray = tr.readByteString();

                if (rsArray.Length != 40)
                    throw new System.IO.IOException("Peer sent corrupt signature");

                if (tr.remain() != 0)
                    throw new System.IO.IOException("Padding in DSA signature!");
            }

            /* Remember, s and r are unsigned ints. */

            byte[] tmp = new byte[20];

            Array.Copy(rsArray, 0, tmp, 0, 20);
            BigInteger r = new BigInteger(1, tmp);

            Array.Copy(rsArray, 20, tmp, 0, 20);
            BigInteger s = new BigInteger(1, tmp);

            if (log.Enabled)
            {
                log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s(" + ((rsArray[20]) & 0xff) + ")");
            }

            return new DSASignature(r, s);
        }

        public static bool verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk)
        {
            /* Inspired by Bouncycastle's DSASigner class */

            SHA1 md = new SHA1();
            md.update(message);
            byte[] sha_message = new byte[md.DigestLength];
            md.digest(sha_message);

            BigInteger m = new BigInteger(1, sha_message);
            BigInteger r = ds.R;
            BigInteger s = ds.S;
            BigInteger g = dpk.G;
            BigInteger p = dpk.P;
            BigInteger q = dpk.Q;
            BigInteger y = dpk.Y;
            BigInteger zero = BigInteger.Zero;

            if (log.Enabled)
            {
                log.log(60, "ssh-dss signature: m: " + m.ToString(16));
                log.log(60, "ssh-dss signature: r: " + r.ToString(16));
                log.log(60, "ssh-dss signature: s: " + s.ToString(16));
                log.log(60, "ssh-dss signature: g: " + g.ToString(16));
                log.log(60, "ssh-dss signature: p: " + p.ToString(16));
                log.log(60, "ssh-dss signature: q: " + q.ToString(16));
                log.log(60, "ssh-dss signature: y: " + y.ToString(16));
            }

            if (zero.CompareTo((System.Object)r) >= 0 || q.CompareTo((System.Object)r) <= 0)
            {
                log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
                return false;
            }

            if (zero.CompareTo((System.Object)s) >= 0 || q.CompareTo((System.Object)s) <= 0)
            {
                log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
                return false;
            }

            BigInteger w = s.ModInverse(q);

            BigInteger u1 = m.Multiply(w).Mod(q);
            BigInteger u2 = r.Multiply(w).Mod(q);

            u1 = g.ModPow(u1, p);
            u2 = y.ModPow(u2, p);

            BigInteger v = u1.Multiply(u2).Mod(p).Mod(q);

            return v.Equals(r);
        }

        public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd)
        {
            SHA1 md = new SHA1();
            md.update(message);
            byte[] sha_message = new byte[md.DigestLength];
            md.digest(sha_message);

            BigInteger m = new BigInteger(1, sha_message);
            BigInteger k;
            int qBitLength = pk.Q.BitLength;

            do
            {
                k = new BigInteger(qBitLength, rnd);
            }
            while (k.CompareTo((System.Object)pk.Q) >= 0);

            BigInteger r = pk.G.ModPow(k, pk.P).Mod(pk.Q);

            k = k.ModInverse(pk.Q).Multiply(m.Add((pk).X.Multiply(r)));

            BigInteger s = k.Mod(pk.Q);

            return new DSASignature(r, s);
        }
        static DSASHA1Verify()
        {
            log = Logger.getLogger(typeof(DSASHA1Verify));
        }
    }
}